#include <basic/errcode.h>
#include <basic/netcm.h>

int sock_set_nonblock(SOCKET sock)
{/*{{{*/
    int flags;

    if((flags=fcntl(sock, F_GETFL, 0)) == -1 ||
            fcntl(sock, F_SETFL, flags|O_NONBLOCK) == -1)
        return(-1);
    return(0);
}/*}}}*/

int sock_get_info(SOCKET sock, struct sock_info *sock_info)
{/*{{{*/
    int ret;
    struct sockaddr_in server_addr;
    socklen_t len;
    union
    {
        int i_val;
        long l_val;
        char c_val[10];
        struct linger linger_val;
        struct timeval timeval_val;
    } val;

    if(sock < 0 || !sock_info)
        return(RET_ERR_PARA);

    memset(&server_addr, 0, sizeof(server_addr));
    len=sizeof(server_addr);
    if(getsockname(sock, (struct sockaddr *)&server_addr, &len) == -1)
    {   ret=-1; goto OUT_ERR;   }
    inet_ntop(AF_INET, &server_addr.sin_addr, sock_info->self_ip,
            sizeof(sock_info->self_ip));
    sock_info->self_port=ntohs(server_addr.sin_port);

    memset(&server_addr, 0, sizeof(server_addr));
    len=sizeof(server_addr);
    if(getpeername(sock, (struct sockaddr *)&server_addr, &len) == -1)
    {   ret=-2; goto OUT_ERR;   }
    inet_ntop(AF_INET, &server_addr.sin_addr, sock_info->peer_ip,
            sizeof(sock_info->peer_ip));
    sock_info->peer_port=ntohs(server_addr.sin_port);

    len=sizeof(val);
    if(getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &val, &len))
    {   ret=-3; goto OUT_ERR;   }
    sock_info->recvbuf_size=val.i_val;
    len=sizeof(val);
    if(getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &val, &len))
    {   ret=-4; goto OUT_ERR;   }
    sock_info->sendbuf_size=val.i_val;

    return(0);

OUT_ERR:
    CLR_BUF(sock_info->self_ip);
    sock_info->self_port=-1;
    CLR_BUF(sock_info->peer_ip);
    sock_info->peer_port=-1;
    sock_info->recvbuf_size=-1;
    sock_info->sendbuf_size=-1;
    return(ret);
}/*}}}*/

char *host_name_to_ip(char *name, char *ip)
{/*{{{*/
    struct addrinfo hints, *res;

    if(!name || !ip)
        return(NULL);

    memset(&hints, 0, sizeof(hints));
    hints.ai_flags=AI_CANONNAME;
    hints.ai_family=AF_INET;
    hints.ai_socktype=SOCK_STREAM;
    if(getaddrinfo(name, NULL, &hints, &res))
        return(NULL);
    inet_ntop(AF_INET,
            &((struct sockaddr_in *)res->ai_addr)->sin_addr,
            ip, IP_SIZE+1);
    freeaddrinfo(res);
    return(ip);
}/*}}}*/

int host_addr_parse(char *addr, struct host_addr_info *info)
{/*{{{*/
    char *addrtmp;
    char *p;
    char *curr, *user_passwd, *host_port;

    if(!addr || !info)
        return(RET_ERR_PARA);

    CLR_BUF(info->protocol);
    CLR_BUF(info->user);
    CLR_BUF(info->passwd);
    CLR_BUF(info->host);
    info->port=0;

    if(!(addrtmp=strdup(addr)))
        return(RET_ERR_ALLOC);
    str_replace(addrtmp, TABS, ZEROS, addrtmp);
    str_replace(addrtmp, SPACES, ZEROS, addrtmp);

    curr=addrtmp;
    if((p=strstr(curr, "//")))
    {
        if(p != curr)
        {
            char *tmp;
            strncpy(info->protocol, curr, p-curr); 
            info->protocol[p-curr]=ZERO;
            if((tmp=strchr(info->protocol, ':')))
                *tmp=ZERO;
        }
        curr=p+2;
    }

    if((p=strchr(curr, '@')))
    {
        *p=ZERO;
        user_passwd=curr;
        host_port=p+1;
    }
    else
    {
        user_passwd=(char *)NULL;
        host_port=curr;
    }
    if(user_passwd)
    {
        if((p=strchr(user_passwd, ':')))
        {
            *p=ZERO;
            strcpy(info->passwd, p+1);
        }
        strcpy(info->user, user_passwd);
    }
    if((p=strchr(host_port, ':')))
    {
        *p=ZERO;
        info->port=atoi(p+1);
    }
    strcpy(info->host, host_port);

    free(addrtmp);
    return(0);
}/*}}}*/

int host_list_parse(char *host_list,
        struct host_addr_info **pready, int *p_ready_num,
        struct host_addr_info **pbackup)
{/*{{{*/
    struct host_addr_info *ready=(struct host_addr_info *)NULL;
    struct host_addr_info *backup=(struct host_addr_info *)NULL;
    char *list=(char *)NULL;
    char **a_out=(char **)NULL;
    char *p;
    char *p_ready;
    char *p_backup;
    int ready_num=0;
    int rtn;
    int res;
    int i;

    if(!pready || !p_ready_num)
        return(RET_ERR_PARA);
    *pready=(struct host_addr_info *)NULL;
    *p_ready_num=0;
    if(pbackup)
        *pbackup=(struct host_addr_info *)NULL;

    if(!(list=strdup(host_list)))
        return(RET_ERR_ALLOC);
    p_ready=list;
    if(pbackup)
    {
        if((p=strchr(list, ',')))
        {
            *p=ZERO;
            p_backup=p+1;
            if((p=strchr(p_backup, ',')))
                *p=ZERO;
            ALLOC_DOERR(backup, struct host_addr_info,
                    rtn=RET_ERR_ALLOC; goto OUT_ERR);
            if((res=host_addr_parse(p_backup, backup)))
            {   rtn=res; goto OUT_ERR;   }
        }
    }
    ready_num=str_split(p_ready, '|', &a_out);
    if(ready_num <= 0)
    {
        if(ready_num > RET_ERR_GENERAL_START)
            rtn=-1;
        else
            rtn=ready_num;
        goto OUT_ERR;
    }
    ALLOC_MULTI_DOERR(ready, struct host_addr_info, ready_num,
            rtn=RET_ERR_ALLOC; goto OUT_ERR);
    for(i=0; i<ready_num; i++)
        if((res=host_addr_parse(a_out[i], &ready[i])))
        {   rtn=res; goto OUT_ERR;   }

    FREE_PTR_ARRAY(a_out, ready_num);
    free(list);
    *pready=ready;
    *p_ready_num=ready_num;
    if(pbackup)
        *pbackup=backup;
    return(0);

OUT_ERR:
    FREE(ready);
    FREE(backup);
    FREE_PTR_ARRAY(a_out, ready_num);
    FREE(list);
    return(rtn);
}/*}}}*/
